/*
 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
 * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc.  All right reserved.
 * Copyright (C) 2011 Google, Inc.  All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#ifndef SKY_ENGINE_PLATFORM_TEXT_BIDIRUNLIST_H_
#define SKY_ENGINE_PLATFORM_TEXT_BIDIRUNLIST_H_

#include "flutter/sky/engine/wtf/Assertions.h"
#include "flutter/sky/engine/wtf/Noncopyable.h"

namespace blink {

template <class Run>
class BidiRunList {
  WTF_MAKE_NONCOPYABLE(BidiRunList);

 public:
  BidiRunList()
      : m_firstRun(0), m_lastRun(0), m_logicallyLastRun(0), m_runCount(0) {}

  // FIXME: Once BidiResolver no longer owns the BidiRunList,
  // then ~BidiRunList should call deleteRuns() automatically.

  Run* firstRun() const { return m_firstRun; }
  Run* lastRun() const { return m_lastRun; }
  Run* logicallyLastRun() const { return m_logicallyLastRun; }
  unsigned runCount() const { return m_runCount; }

  void addRun(Run*);
  void prependRun(Run*);

  void moveRunToEnd(Run*);
  void moveRunToBeginning(Run*);

  void deleteRuns();
  void reverseRuns(unsigned start, unsigned end);
  void reorderRunsFromLevels();

  void setLogicallyLastRun(Run* run) { m_logicallyLastRun = run; }

  void replaceRunWithRuns(Run* toReplace, BidiRunList<Run>& newRuns);

 private:
  void clearWithoutDestroyingRuns();

  Run* m_firstRun;
  Run* m_lastRun;
  Run* m_logicallyLastRun;
  unsigned m_runCount;
};

template <class Run>
inline void BidiRunList<Run>::addRun(Run* run) {
  if (!m_firstRun)
    m_firstRun = run;
  else
    m_lastRun->m_next = run;
  m_lastRun = run;
  m_runCount++;
}

template <class Run>
inline void BidiRunList<Run>::prependRun(Run* run) {
  ASSERT(!run->m_next);

  if (!m_lastRun)
    m_lastRun = run;
  else
    run->m_next = m_firstRun;
  m_firstRun = run;
  m_runCount++;
}

template <class Run>
inline void BidiRunList<Run>::moveRunToEnd(Run* run) {
  ASSERT(m_firstRun);
  ASSERT(m_lastRun);
  ASSERT(run->m_next);

  Run* current = 0;
  Run* next = m_firstRun;
  while (next != run) {
    current = next;
    next = current->next();
  }

  if (!current)
    m_firstRun = run->next();
  else
    current->m_next = run->m_next;

  run->m_next = 0;
  m_lastRun->m_next = run;
  m_lastRun = run;
}

template <class Run>
inline void BidiRunList<Run>::moveRunToBeginning(Run* run) {
  ASSERT(m_firstRun);
  ASSERT(m_lastRun);
  ASSERT(run != m_firstRun);

  Run* current = m_firstRun;
  Run* next = current->next();
  while (next != run) {
    current = next;
    next = current->next();
  }

  current->m_next = run->m_next;
  if (run == m_lastRun)
    m_lastRun = current;

  run->m_next = m_firstRun;
  m_firstRun = run;
}

template <class Run>
void BidiRunList<Run>::replaceRunWithRuns(Run* toReplace,
                                          BidiRunList<Run>& newRuns) {
  ASSERT(newRuns.runCount());
  ASSERT(m_firstRun);
  ASSERT(toReplace);

  if (m_firstRun == toReplace) {
    m_firstRun = newRuns.firstRun();
  } else {
    // Find the run just before "toReplace" in the list of runs.
    Run* previousRun = m_firstRun;
    while (previousRun->next() != toReplace)
      previousRun = previousRun->next();
    ASSERT(previousRun);
    previousRun->setNext(newRuns.firstRun());
  }

  newRuns.lastRun()->setNext(toReplace->next());

  // Fix up any of other pointers which may now be stale.
  if (m_lastRun == toReplace)
    m_lastRun = newRuns.lastRun();
  if (m_logicallyLastRun == toReplace)
    m_logicallyLastRun = newRuns.logicallyLastRun();
  m_runCount +=
      newRuns.runCount() - 1;  // We added the new runs and removed toReplace.

  delete toReplace;
  newRuns.clearWithoutDestroyingRuns();
}

template <class Run>
void BidiRunList<Run>::clearWithoutDestroyingRuns() {
  m_firstRun = 0;
  m_lastRun = 0;
  m_logicallyLastRun = 0;
  m_runCount = 0;
}

template <class Run>
void BidiRunList<Run>::deleteRuns() {
  if (!m_firstRun)
    return;

  Run* curr = m_firstRun;
  while (curr) {
    Run* s = curr->next();
    delete curr;
    curr = s;
  }

  clearWithoutDestroyingRuns();
}

template <class Run>
void BidiRunList<Run>::reverseRuns(unsigned start, unsigned end) {
  ASSERT(m_runCount);
  if (start >= end)
    return;

  ASSERT(end < m_runCount);

  // Get the item before the start of the runs to reverse and put it in
  // |beforeStart|. |curr| should point to the first run to reverse.
  Run* curr = m_firstRun;
  Run* beforeStart = 0;
  unsigned i = 0;
  while (i < start) {
    i++;
    beforeStart = curr;
    curr = curr->next();
  }

  Run* startRun = curr;
  while (i < end) {
    i++;
    curr = curr->next();
  }
  Run* endRun = curr;
  Run* afterEnd = curr->next();

  i = start;
  curr = startRun;
  Run* newNext = afterEnd;
  while (i <= end) {
    // Do the reversal.
    Run* next = curr->next();
    curr->m_next = newNext;
    newNext = curr;
    curr = next;
    i++;
  }

  // Now hook up beforeStart and afterEnd to the startRun and endRun.
  if (beforeStart)
    beforeStart->m_next = endRun;
  else
    m_firstRun = endRun;

  startRun->m_next = afterEnd;
  if (!afterEnd)
    m_lastRun = startRun;
}

}  // namespace blink

#endif  // SKY_ENGINE_PLATFORM_TEXT_BIDIRUNLIST_H_
